package com.iot08.api.utils;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.maps.GeoApiContext;
import com.google.maps.GeocodingApi;
import com.google.maps.GeolocationApi;
import com.google.maps.model.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 谷哥地图API调用
 */
@Slf4j
@Component
public class GoogleMapsApi {

    @Value("goolge.googleGeoApiKey:")
    private String googleGeoApiKey;


    private static GeoApiContext context = null;


    private GeoApiContext getGeoApiContext(){
        if(context == null){
            context = new GeoApiContext.Builder()
                    .connectTimeout(15, TimeUnit.SECONDS)//.disableRetries()
                    .maxRetries(1)
                    .queryRateLimit(300)
                    .readTimeout(15, TimeUnit.SECONDS)
                    .writeTimeout(15, TimeUnit.SECONDS)
                    .apiKey(googleGeoApiKey)
                    .build(); // first key
        }
        return context;
    }

    /**
     * 反向地理编码
     * @param deviceId
     * @param latLng
     * @param language
     * @return
     */
    public String geocoding(String deviceId, final LatLng latLng, final String language) {
        Long cost = System.currentTimeMillis();
        log.info("Start geocoding,deviceId:{}", deviceId);
        String address = "";
        try {
            GeocodingResult[] results = GeocodingApi.newRequest(getGeoApiContext())
                    .latlng(latLng).language(language).await();

            if (results != null && results.length > 0) {
                address = results[0].formattedAddress;
            }
        } catch (Exception ex) {
            log.error("Geocoding<EX>,deviceId:{}, msg:", deviceId, ex.getMessage());
        }
        log.info("Finished geocoding,deviceId:{},cost:{}", deviceId, System.currentTimeMillis() - cost);
        return address;
    }

    /**
     * 根据WiFI和基站信息获取位置和精确半径
     * @param deviceId         imei
     * @param mcc              所属国家代码
     * @param mnc              移动网号
     * @param radioType        移动无线电类型 默认 gsm
     * @param cellTowers       基站信息
     * @param wifiAccessPoints Wifi信息
     */
    public GeolocationResult geolocation(String deviceId,
                                                 int mcc,
                                                 int mnc,
                                                 String radioType,
                                                 CellTower[] cellTowers,
                                                 WifiAccessPoint[] wifiAccessPoints) {
        Long cost = System.currentTimeMillis();
        log.info("Start geolocation, deviceId:{}", deviceId);
        GeolocationResult result = null;
        try {
            result = GeolocationApi.newRequest(getGeoApiContext())
                    .ConsiderIp(false)
                    .HomeMobileCountryCode(mcc)
                    .HomeMobileNetworkCode(mnc)
                    .RadioType(radioType) //"gsm"
                    //.Carrier("T-Mobile")
                    .CellTowers(cellTowers) //可以设为null
                    .WifiAccessPoints(wifiAccessPoints) //可以设为null
                    .CreatePayload()
                    .await();
        } catch (Exception ex) {
            log.error("Geolocation<EX>,deviceId:{}, msg:", deviceId, ex.getMessage());
            ex.printStackTrace();
        }
        log.info("Finished geolocation, deviceId:{}, cost:{}", deviceId, System.currentTimeMillis() - cost);
        return result;
    }

    /**
     * 转换非CDMA的基站信息
     *
     * @param cells
     *     [
     *         {
     *             "mcc"  : <int> //国家编号
     *             "mnc"  : <int> //运营商编号
     *             "lac"  : <int> //小区编号
     *             "ci"   : <int> //基站编号
     *             "rxlev": <int> //信号频点,该值减去110后得到信号强度,一般为负数
     *             "timestamp": <long> //扫描到基站时的unix时间戳 单位:秒
     *             "connected"*: <int> //是否是连接基站 0:否 1:是
     *             "type"*: <string> //基站网络类型 只能是以下值:gsm, wcdma, lte, 对应(2G)/(3G)/(4G)
     *         },
     *         ...
     *     ]
     * @return
     */
    public CellTower[] parseNotCDMACellTowers(JSONArray cells) {
//        if(cells == null || cells.size() == 0){ return null; }
        CellTower[] cellTowers = new CellTower[cells.size()];
        for (int i = 0; i < cells.size(); i++) {
            JSONObject itemCell = cells.getJSONObject(i);
            CellTower cellTower = new CellTower.CellTowerBuilder()
                    .MobileCountryCode(itemCell.getInteger("mcc")) //所属国家代码
                    .MobileNetworkCode(itemCell.getInteger("mnc")) //移动网号
                    .CellId(itemCell.getInteger("ci"))            //基站小区编号
                    .LocationAreaCode(itemCell.getInteger("lac"))  //位置区域码
                    .SignalStrength(itemCell.getInteger("rxlev"))   //信号强度
                    .Age(0)
                    .createCellTower();

            cellTowers[i] = cellTower;

        }
        return cellTowers;
    }

    /**
     * 转换CDMA的基站信息
     *
     * @param telecomcell
     *     {
     *         "sid"     : < integer>//系统识别码  取值范围 : 1-32767
     *         "nid"     : < integer >//网络识别码 取值范围 : 0-65535
     *         "bid"   	: <int>//小区唯一识别码 取值范围 : 1-65535
     *         "signal"	: <int>//信号频点
     *     }
     * @return
     */
    public CellTower[] parseCDMACellTowers(JSONObject telecomcell) {
//        if(telecomcell == null || telecomcell.size() == 0){ return null; }
        Integer sid = telecomcell.getInteger("sid");
        Integer nid = telecomcell.getInteger("nid");
        Integer bid = telecomcell.getInteger("bid");
//        if (sid == 0 && nid == 0 && bid == 0) { return null; }
        CellTower[] cellTowers = new CellTower[1];

        cellTowers[0] = new CellTower.CellTowerBuilder()
                .CellId(bid)            //基站小区编号
                .LocationAreaCode(nid)  //位置区域码
                .MobileNetworkCode(sid) //系统识别码
                .SignalStrength(telecomcell.getInteger("signal"))   //信号强度
                .Age(0)
                .createCellTower();

        return cellTowers;
    }

    /**
     * 转换Wifi信息
     *
     * @param wifis
     *      [
     *         {
     *             "mac"   			: <string> //wifi的mac地址 eg: "14:de:39:39:08:81"
     *             "signal"			: <string> //信号强度
     *             "ssid"  			: <string>
     *             "frequency"* 	: <int> //指定通道下的频率，单位: MHz
     *             "channel"* 		: <int> //wifi 子频段的通道编号
     *             "connected"* 	: <int> //是否是连接的 WIFI 1: 连接 非连接时不传此参数
     *         },
     *         ...
     *     ]
     *
     * @return
     */
    public WifiAccessPoint[] parseWifiAccessPoints(JSONArray wifis) {
        if (wifis.size() == 0) { return null; }
        WifiAccessPoint[] wifiAccessPoints = new WifiAccessPoint[wifis.size()];

        for (int i = 0; i < wifis.size(); i++) {
            JSONObject itemWifi = wifis.getJSONObject(i);
            WifiAccessPoint wifiAccessPoint = new WifiAccessPoint.WifiAccessPointBuilder()
                    .MacAddress(itemWifi.getString("mac"))
                    .SignalStrength(Integer.parseInt(itemWifi.getString("signal")))
                    .createWifiAccessPoint();
            Integer channel = itemWifi.getInteger("channel");
            if(channel != null){
                wifiAccessPoint.channel = channel;
            }

            wifiAccessPoints[i] = wifiAccessPoint;
        }
        return wifiAccessPoints;
    }


    public static void main(String[] args) {

        //模拟 海外上传的基站信息及 GPS点  lngLat : -81.37,28.73
        //lat:28.732701566666666 lng:-81.370657
        /*
        mcc#mnc#cellId#lac#signal
        	"cellTower": "136#1a#101d903#7f68#65",
        	"nearByCellTowers": ["c1e#3c#101d90d#843a#-87"],
        格式：
            macAddress,ssid,signal
            逗号分隔的字串组成的数组
            [[macAddress,ssid,signal
            ]]

            "wifiPoints": [
            ["f4:92:bf:6a:72:84", "wlan-guest", "-74"],
            ["bc:e9:2f:77:65:38", "DIRECT-37-HP OfficeJet Pro 6970", "-69"],
            ["48:dd:0c:7e:da:05", "Main", "-49"],
            ["e0:22:03:67:b7:2c", "", "-43"],
            ["48:dd:0c:81:1f:65", "Main", "-43"],
            ["14:59:c0:93:96:6a", "Metro_LM_iOT", "-30"]
            ],

        {
            "type": 7,
            "ident": 777777,
            "cells": [
                {
                    "mcc": 310,
                    "mnc": 26,
                    "lac": 32616,
                    "ci": 16898307,
                    "rxlev": 65,
                    "timestamp": 1644940800,
                    "connected": 1,
                    "type": "lte"
                },
                {
                    "mcc": 3102,
                    "mnc": 60,
                    "lac": 33850,
                    "ci": 16898317,
                    "rxlev": -87,
                    "timestamp": 1644940800,
                    "type": "lte"
                }
            ],
            "wifis": [
                {
                    "mac": "f4:92:bf:6a:72:84",
                    "signal": "-74",
                    "ssid": "wlan-guest"
                },
                {
                    "mac": "bc:e9:2f:77:65:38",
                    "signal": "-69",
                    "ssid": "DIRECT-37-HP OfficeJet Pro 6970"
                },
                {
                    "mac": "48:dd:0c:7e:da:05",
                    "signal": "-49",
                    "ssid": "Main"
                },
                {
                    "mac": "e0:22:03:67:b7:2c",
                    "signal": "-43",
                    "ssid": ""
                },
                {
                    "mac": "48:dd:0c:81:1f:65",
                    "signal": "-43",
                    "ssid": "Main"
                },
                {
                    "mac": "14:59:c0:93:96:6a",
                    "signal": "-30",
                    "ssid": "Metro_LM_iOT"
                }
            ],
            "battery": 79,
            "electricityStatus": 0,
            "timestamp": 1644940800,
            "sos": 0,
            "feedback": 1
        }
        */

        String tempType7Str = "{\"type\":7,\"ident\":777777,\"mcc\":310,\"cells\":[{\"mcc\":310,\"mnc\":26,\"lac\":32616,\"ci\":16898307,\"rxlev\":65,\"timestamp\":1644940800,\"connected\":1,\"type\":\"lte\"},{\"mcc\":3102,\"mnc\":60,\"lac\":33850,\"ci\":16898317,\"rxlev\":-87,\"timestamp\":1644940800,\"type\":\"lte\"}],\"wifis\":[{\"mac\":\"f4:92:bf:6a:72:84\",\"signal\":\"-74\",\"ssid\":\"wlan-guest\"},{\"mac\":\"bc:e9:2f:77:65:38\",\"signal\":\"-69\",\"ssid\":\"DIRECT-37-HP OfficeJet Pro 6970\"},{\"mac\":\"48:dd:0c:7e:da:05\",\"signal\":\"-49\",\"ssid\":\"Main\"},{\"mac\":\"e0:22:03:67:b7:2c\",\"signal\":\"-43\",\"ssid\":\"\"},{\"mac\":\"48:dd:0c:81:1f:65\",\"signal\":\"-43\",\"ssid\":\"Main\"},{\"mac\":\"14:59:c0:93:96:6a\",\"signal\":\"-30\",\"ssid\":\"Metro_LM_iOT\"}],\"battery\":79,\"electricityStatus\":0,\"timestamp\":1644940800,\"sos\":0,\"feedback\":1}";
        JSONObject message = JSONObject.parseObject(tempType7Str);
        Integer mcc = message.getInteger("mcc");
        if (mcc == null) {
            mcc = 460;
        }
        String locationType = "LBS";
        Integer mncOrSid = 0;
        String radioType = "gsm";
        GoogleMapsApi googleMapsApi = new GoogleMapsApi();
        CellTower[] cellTowers = googleMapsApi.parseNotCDMACellTowers(message.getJSONArray("cells"));
        if(cellTowers == null){
            cellTowers = googleMapsApi.parseCDMACellTowers(message.getJSONObject("telecomcell"));
            radioType = "cdma";
            mncOrSid = message.getJSONObject("telecomcell").getInteger("sid");
        }else{
            JSONObject cell = message.getJSONArray("cells").getJSONObject(0);
            String type = cell.getString("type");
            if(type != null){ radioType = type; }
            mncOrSid = cell.getInteger("mnc");
        }
        WifiAccessPoint[] wifiAccessPoints = googleMapsApi.parseWifiAccessPoints(message.getJSONArray("wifis"));
        if (wifiAccessPoints != null) {
            locationType = "WIFI";
        }
        GeolocationResult geolocationResult = googleMapsApi.geolocation("测试IMEI", mcc, mncOrSid, radioType, cellTowers, wifiAccessPoints);
        System.out.println(String.format("geolocationResult: %s", geolocationResult.toString()));

    }

}
